package main

import (
	"fmt"
	"knowledge-go/cpt/c_000_com"
	"sort"
)

/*
Slice 操作与数组非常相似
Slice（切片）代表变长的序列，序列中每个元素都有相同的类型
slice的底层确实引用一个数组对象。
一个slice由三个部分构成：指针、长度和容量。
指针指向第一个slice元素对应的底层数组元素的地址
*/
func main() {
	// 1.索引:元素
	months := []string{
		1: "一月", 2: "二月", 3: "三月", 4: "四月", 5: "五月", 6: "六月",
		7: "七月", 8: "八月", 9: "九月", 10: "十月", 11: "十一月", 12: "十二月"}
	fmt.Println(months)

	// 2.反转
	reverse(months)
	fmt.Println("反转", months)

	// 3.默认初始化的不是null,而是 [],长度为0
	var a1 []string
	reverse(a1)
	fmt.Println("反转2", a1)

	// 4.内置的make函数创建一个指定元素类型、长度和容量的slice。容量部分可以省略，在这种情况下，容量将等于长度。
	m1 := make([]string, 5)
	m2 := make([]string, 5, 10)
	fmt.Println(m1, m2)

	// 5.增加元素
	var arr []int
	arr2 := append(arr, 1)
	arr3 := append(arr2, 1)
	// arr2 和 arr3 的引用变化了
	fmt.Println(arr, arr2, c_000_com.GetSliceAddr(arr2), c_000_com.GetSliceAddr(arr3))

	// 6.复制
	cp1 := []int{1, 2, 3}
	cp2 := make([]int, len(cp1), 2*len(cp1))
	copy(cp2, cp1)
	fmt.Println(cp1, cp2)

	// 7.观察cap的变化,和Java的ArrayList一样，当cap满的时候才进行扩容，扩容系数为2倍
	watchSlice()

	/*
		slice并不是一个纯粹的引用类型，它实际上是一个类似下面结构体的聚合类型
		type IntSlice struct {
			ptr      *[]int // 底层数组的指针
			len, cap int
		}
	*/

	// 8、排序
	a8 := []int{9, 1, 8}
	sort.Ints(a8)
	fmt.Println(a8)

}

func reverse(arr []string) {
	l := len(arr)
	last := l - 1
	for i := 0; i < last; i++ {
		arr[i], arr[last] = arr[last], arr[i]
		last--
	}
}

/*
	模拟 append 底层实现

每次调用appendInt函数，必须先检测slice底层数组是否有足够的容量来保存新添加的元素。
如果有足够空间的话，直接扩展slice（依然在原有的底层数组之上），将新添加的y元素复制到新扩展的空间，并返回slice。
因此，输入的x和输出的z共享相同的底层数组。

如果没有足够的增长空间的话，appendInt函数则会先分配一个足够大的slice用于保存新的结果，先将输入的x复制到新的空间，然后添加y元素。
结果z和输入的x引用的将是不同的底层数组。
*/
func appendInt(x []int, y int) []int {
	var z []int
	zlen := len(x) + 1
	if zlen <= cap(x) {
		// There is room to grow.  Extend the slice.
		z = x[:zlen]
	} else {
		// There is insufficient space.  Allocate a new array.
		// Grow by doubling, for amortized linear complexity.
		zcap := zlen
		if zcap < 2*len(x) {
			zcap = 2 * len(x)
		}
		z = make([]int, zlen, zcap)
		copy(z, x) // a built-in function; see text
	}
	z[len(x)] = y
	return z
}

/*
观察cap的变化
*/
func watchSlice() {
	var x, y []int
	for i := 0; i < 10; i++ {
		y = appendInt(x, i)
		fmt.Printf("%d cap=%d\t%v\n", i, cap(y), y)
		x = y
	}
}
